package com.itany.corejava.code12_线程;

/**
 * @author 石小俊
 * @date 2023年05月16日 10:24
 */
public class Test09_生产者消费者问题 {
    public static void main(String[] args) {
        ProductPool pool = new ProductPool();
        // 定义生产者
        Producer p1 = new Producer(pool, "生产者p1");
        Producer p2 = new Producer(pool, "生产者p2");
        Producer p3 = new Producer(pool, "生产者p3");
        // 定义消费者
        Consumer c1 = new Consumer(pool, "消费者c1");
        Consumer c2 = new Consumer(pool, "消费者c2");
        // 启动线程
        p1.start();
        p2.start();
        p3.start();
        c1.start();
        c2.start();

    }
}

/**
 * 商品缓冲区
 */
class ProductPool {
    // 当前缓冲区的商品数量
    private int num;
    // 缓冲区商品数量的上限,常量
    private static final int MAX_COUNT = 20;

    /**
     * 生产商品
     * 生产者生产商品后向缓冲区存放商品
     * 每调用一次方法,表示存放一件商品
     *
     * @return 当前缓冲区商品数量
     */
    public int put() {
        // 每存放一次,表示商品数量+1
        num++;
        return num;
    }

    /**
     * 消费商品
     * 消费者从缓冲区中拿出商品
     * 每调用一次方法,表示拿出一件商品
     *
     * @return 当前缓冲区商品数量
     */
    public int get() {
        num--;
        return num;
    }

    /**
     * 判断当前缓冲区商品数量是否达到了上限
     *
     * @return
     */
    public boolean isFull() {
        return this.num == MAX_COUNT;
    }

    /**
     * 判断电气概念缓冲区商品数量是否为空
     *
     * @return
     */
    public boolean isEmpty() {
        return this.num == 0;
    }
}

/**
 * 生产者线程
 */
class Producer extends Thread {
    private ProductPool pool;
    private String name; // 生产者线程名

    public Producer(ProductPool pool, String name) {
        this.pool = pool;
        this.name = name;
    }

    public void run() {
        while (true) {
            synchronized (pool) {
                // 根据缓冲区商品是否达到上限判断是否需要生产
                if (pool.isFull()) {
                    // 如果缓冲区商品达到了上限,则停止生产,等待消费
                    try {
                        pool.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                } else {
                    // 如果缓冲区商品没有达到上限,则生产商品
                    int num = pool.put();
                    System.out.println(this.name + "生产了一件商品,当前缓冲区商品数量为:" + num);
                    // 生产了商品之后缓冲区一定不为空,需要消费者继续消费
                    // 由于缓冲区为空后,消费者会停止消费
                    // 因此,生产了商品之后需要唤醒消费者
                    pool.notifyAll();
                }
            }
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

/**
 * 消费者线程
 */
class Consumer extends Thread {
    private ProductPool pool;
    private String name; // 消费者线程名

    public Consumer(ProductPool pool, String name) {
        this.pool = pool;
        this.name = name;
    }

    public void run() {
        while (true) {
            synchronized (pool) {
                // 根据缓冲区商品是否为空判断是否需要消费
                if (pool.isEmpty()) {
                    // 如果缓冲区商品为空,则停止消费,需要生产者进行生产
                    try {
                        pool.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                } else {
                    // 当缓冲区商品不为空,则消费商品
                    int num = pool.get();
                    System.out.println(this.name + "消费了一件商品,当前缓冲区商品数量为:" + num);
                    // 消费了商品之后,需要唤醒生产者继续生产
                    pool.notifyAll();
                }
            }
            try {
                Thread.sleep(600);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

